package cn.mazexal.examapi.services;

import cn.hutool.core.util.IdUtil;
import cn.mazexal.examapi.common.models.BizException;
import cn.mazexal.examapi.common.models.CustomerError;
import cn.mazexal.examapi.dataware.*;
import cn.mazexal.examapi.models.bo.MenuQuestionTypeConfig;
import cn.mazexal.examapi.models.converter.*;
import cn.mazexal.examapi.models.db.*;
import cn.mazexal.examapi.models.db.other.ThreeStatisticDO;
import cn.mazexal.examapi.models.enums.ConfigItemEnums;
import cn.mazexal.examapi.models.enums.MenuTypeEnum;
import cn.mazexal.examapi.models.enums.QuestionTypeEnum;
import cn.mazexal.examapi.models.enums.VipPriceOutTypeEnum;
import cn.mazexal.examapi.models.redisKeys.RedisCommonKey;
import cn.mazexal.examapi.models.vo.*;
import cn.mazexal.examapi.utils.qiniu.QiniuOssUtil;
import com.alibaba.fastjson2.JSON;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author: Mazexal
 * Date: 2025/5/11
 */
@Service
@Slf4j
public class CommonService {

    @Resource
    private RedisService redisService;

    @Resource
    private ConfigMapper configMapper;

    @Resource
    private ExamBankMapper examBankMapper;

    @Resource
    private CardMenuMapper cardMenuMapper;

    @Resource
    private ExamBankConverter examBankConverter;

    @Resource
    private CardMenuConverter cardMenuConverter;

    @Resource
    private UserFeedbackConverter userFeedbackConverter;

    @Resource
    private UserFeedbackMapper userFeedbackMapper;

    @Resource
    private VideoMenuMapper videoMenuMapper;

    @Resource
    private VideoMenuConverter videoMenuConverter;

    @Resource
    private VideoMenuIntroMapper videoMenuIntroMapper;

    @Resource
    private VideoMenuIntroConverter videoMenuIntroConverter;

    @Resource
    private UserWrongMapper userWrongMapper;

    @Resource
    private UserCollectMapper userCollectMapper;

    @Resource
    private StatisticMapper statisticMapper;

    @Resource
    private SysChannelVersionMapper sysChannelVersionMapper;

    @Resource
    private SysChannelMapper sysChannelMapper;

    @Resource
    private VipPriceMapper vipPriceMapper;

    @Resource
    private VipPriceConverter vipPriceConverter;
    @Resource
    private SysPayConfigMapper sysPayConfigMapper;

    @Resource
    private SysBroadcastMapper sysBroadcastMapper;

    @Resource
    private SysBroadcastConverter sysBroadcastConverter;

    @Resource
    private QiniuOssUtil qiniuOssUtil;

    /**
     * 获取初始化配置
     *
     * @return
     */
    public InitConfig getInitConfig() {
        log.info("获取初始化配置");
        String str = redisService.get(RedisCommonKey.INIT_CONFIG);
        if (StringUtils.isBlank(str)) {
            InitConfig initConfig = new InitConfig();
            initConfig.setExamBankList(getExamBankVOList());
            initConfig.setBannerList(getBannerItemVOList());
            redisService.set(RedisCommonKey.INIT_CONFIG, JSON.toJSONString(initConfig), 15, TimeUnit.SECONDS);
            return initConfig;
        } else {
            return JSON.parseObject(str, InitConfig.class);
        }
    }

    /**
     * 获取题库列表
     *
     * @return
     */
    public List<ExamBankVO> getExamBankVOList() {
        List<ExamBankDO> examBankDOS = examBankMapper.selectList(null);
        if (examBankDOS == null) {
            return new ArrayList<>();
        }
        return examBankDOS.stream().map(examBankConverter::toView).toList();
    }

    /**
     * 获取banner列表
     *
     * @return
     */
    public List<BannerItemVO> getBannerItemVOList() {
        ConfigDO config = configMapper.selectById(ConfigItemEnums.Banner_Config.getCode());
        if (config == null) {
            return new ArrayList<>();
        }
        return JSON.parseArray(config.getContext(), BannerItemVO.class);
    }


    public ConfigDO getConfigDOById(ConfigItemEnums configItem) {
        return configMapper.selectById(configItem.getCode());
    }

    /**
     * 获取卡组菜单
     *
     * @return
     */
    public List<CardMenuVO> getCardMenuVOList(Long examBankId) {
        // 这里要返回cnt以及questionType, 首页id为-1, 配置单选多选简答菜单.
        String key = RedisCommonKey.getCardMenuKey(examBankId);
        String str = redisService.get(key);
        if (StringUtils.isBlank(str)) {
            List<Integer> menuQuestionTypeConfig = getMenuQuestionTypeConfig(examBankId);
            // 这里获取到了以后, 封装为各个题型
            List<CardMenuVO> res = new ArrayList<>();
            Map<String, Long> cardMenuStatistic = getCardMenuStatistic(examBankId);

            for (Integer questionType : menuQuestionTypeConfig) {
                QuestionTypeEnum questionTypeEnum = QuestionTypeEnum.getByCode(questionType);
                CardMenuVO item = buildCardMenuVO(questionTypeEnum);
                item.setChildren(getOrgCardMenu(examBankId, questionType, cardMenuStatistic));
                if (CollectionUtils.isNotEmpty(item.getChildren())) {
                    res.add(item);
                }
            }
            redisService.set(key, JSON.toJSONString(res), 60, TimeUnit.SECONDS);
            return res;
        } else {
            return JSON.parseArray(str, CardMenuVO.class);
        }
    }

    public List<CardMenuVO> getOrgCardMenu(Long examBankId, int questionType, Map<String, Long> cardMenuStatistic) {
        String key = RedisCommonKey.getCardMenuKeyOrg(examBankId, questionType);
        String str = redisService.get(key);
        if (StringUtils.isBlank(str)) {
            List<CardMenuDO> cardMenuDOS = cardMenuMapper.getCardMenuList(examBankId);
            List<CardMenuVO> list = cardMenuDOS.stream().map(cardMenuConverter::toView).toList();
            for (CardMenuVO item : list) {
                item.setQuestionType(questionType);
                item.setCnt(cardMenuStatistic.getOrDefault(questionType + ":" + item.getId(), 0L).intValue());
            }
            list = new ArrayList<>(list);
            // 移除list中cnt为0并且类型为文件的
            list.removeIf(item -> item.getMenuType() == MenuTypeEnum.OPTION.getCode() && item.getCnt() == 0);
            List<CardMenuVO> res = buildCardMenuVoList(list);
            // 然后移出文件夹中cnt为0的
            subRemove(res);
            redisService.set(key, JSON.toJSONString(res), 60, TimeUnit.SECONDS);
            return res;
        } else {
            return JSON.parseArray(str, CardMenuVO.class);
        }
    }

    public void subRemove(List<CardMenuVO> res) {
        if (CollectionUtils.isEmpty(res)) {
            return;
        }
        // 递归移出文件夹中cnt为0的
        res.removeIf(item -> item.getMenuType() == MenuTypeEnum.FOLDER.getCode() && item.getCnt() == 0);
        if (CollectionUtils.isEmpty(res)) {
            return;
        }
        for (CardMenuVO item : res) {
            subRemove(item.getChildren());
        }
    }

    public Map<String, Long> getCardMenuStatistic(Long examBankId) {
        List<ThreeStatisticDO> res = statisticMapper.getQuestionTypeAndMenuList(examBankId);
        Map<String, Long> resMap = new HashMap<>();
        res.forEach(item -> {
            // questionType : menuId  作为key, cnt 作为value
            resMap.put(item.getIdOne() + ":" + item.getIdTwo(), item.getRes());
        });
        return resMap;
    }


    private CardMenuVO buildCardMenuVO(QuestionTypeEnum questionTypeEnum) {
        CardMenuVO res = new CardMenuVO();
        res.setId(-1L);
        res.setName(questionTypeEnum.getDesc());
        res.setQuestionType(questionTypeEnum.getCode());
        return res;
    }

    public Map<Long, MenuQuestionTypeConfig> getMenuQuestionTypeConfigMap() {
        ConfigDO menuConfig = getConfigDOById(ConfigItemEnums.Menu_Config);
        List<MenuQuestionTypeConfig> menuQuestionTypeConfigs = JSON.parseArray(menuConfig.getContext(),
                MenuQuestionTypeConfig.class);
        return menuQuestionTypeConfigs.stream().collect(Collectors.toMap(MenuQuestionTypeConfig::getId, v -> v));
    }

    public List<Integer> getMenuQuestionTypeConfig(Long menuId) {
        Map<Long, MenuQuestionTypeConfig> menuQuestionTypeConfigMap = getMenuQuestionTypeConfigMap();
        MenuQuestionTypeConfig menuQuestionTypeConfig = menuQuestionTypeConfigMap.get(menuId);
        if (menuQuestionTypeConfig == null) {
            // 如果找不到, 返回默认值
            menuQuestionTypeConfig = menuQuestionTypeConfigMap.get(0L);
        }
        return menuQuestionTypeConfig.getQuestionTypeList();
    }


    private List<CardMenuVO> buildCardMenuVoList(List<CardMenuVO> input) {
        Map<Long, CardMenuVO> map = input.stream().collect(Collectors.toMap(CardMenuVO::getId, v -> v));
        List<CardMenuVO> res = new ArrayList<>();
        for (CardMenuVO cardMenuVO : input) {
            // 如果父ID为0，说明是顶层节点，直接添加到结果列表中
            if (cardMenuVO.getParentId() == 0) {
                res.add(cardMenuVO);
            } else {
                CardMenuVO parent = map.get(cardMenuVO.getParentId());
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(cardMenuVO);
                }
            }
        }
        return res;
    }


    public List<Long> getSubMenuIds(Long menuId, Long examBankId) {
        String key = RedisCommonKey.getSubMenuIdsKey(examBankId, menuId);
        String str = redisService.get(key);
        if (StringUtils.isNotBlank(str)) {
            return JSON.parseArray(str, Long.class);
        } else {
            List<CardMenuVO> cardMenuVOList = getCardMenuVOList(examBankId);
            List<CardMenuVO> subCardMenu = getSubCardMenu(menuId, cardMenuVOList);
            List<Long> res = new ArrayList<>();
            res.add(menuId);
            doGetSubMenuIds(res, subCardMenu);
            redisService.set(key, JSON.toJSONString(res), 600, TimeUnit.SECONDS);
            return res;
        }
    }

    public List<CardMenuVO> getSubCardMenu(Long menuId, List<CardMenuVO> input) {
        for (CardMenuVO cardMenuVO : input) {
            if (cardMenuVO.getId().equals(menuId)) {
                return cardMenuVO.getChildren();
            }
            if (CollectionUtils.isNotEmpty(cardMenuVO.getChildren())) {
                // 递归查找
                List<CardMenuVO> tmp = getSubCardMenu(menuId, cardMenuVO.getChildren());
                if (tmp != null) {
                    return tmp;
                }
            }
        }
        return null;
    }

    // 递归获取子菜单的ID
    public void doGetSubMenuIds(List<Long> res, List<CardMenuVO> input) {
        if (CollectionUtils.isNotEmpty(input)) {
            for (CardMenuVO cardMenuVO : input) {
                res.add(cardMenuVO.getId());
                if (CollectionUtils.isNotEmpty(cardMenuVO.getChildren())) {
                    doGetSubMenuIds(res, cardMenuVO.getChildren());
                }
            }
        }
    }

    public List<CardMenuVO> getMainCardMenuVOList(Long examBankId) {
        String key = RedisCommonKey.getMainCardMenuKey(examBankId);
        String str = redisService.get(key);
        if (StringUtils.isNotBlank(str)) {
            return JSON.parseArray(str, CardMenuVO.class);
        }
        List<CardMenuDO> cardMenuDOS = cardMenuMapper.getMainCardMenuList(examBankId);
        if (cardMenuDOS == null) {
            return Collections.EMPTY_LIST;
        }
        List<CardMenuVO> res = cardMenuDOS.stream().map(cardMenuConverter::toView).toList();
        redisService.set(key, JSON.toJSONString(res), 60, TimeUnit.SECONDS);
        return res;
    }

    // 获取最顶层的菜单ID
    public Long getTopMenuIdByPath(String path) {
        String[] arr = path.split("/");
        return Long.valueOf(arr[0]);
    }

    public CardMenuDO getCardMenuDOById(Long menuId) {
        return cardMenuMapper.selectById(menuId);
    }

    public Map<Long, CardMenuDO> getCardMenuDOMapByIds(Set<Long> menuIds) {
        List<CardMenuDO> cardMenuDOS = cardMenuMapper.selectByIds(menuIds);
        if (cardMenuDOS == null) {
            return Collections.EMPTY_MAP;
        }
        return cardMenuDOS.stream().collect(Collectors.toMap(CardMenuDO::getId, v -> v));
    }

    public CardMenuDO getMenuById(Long menuId) {
        return cardMenuMapper.selectById(menuId);
    }

    public void addFeedback(UserFeedbackVO userFeedbackVO) {
        UserFeedbackDO db = userFeedbackConverter.toDb(userFeedbackVO);
        db.setId(IdUtil.getSnowflakeNextId());
        db.setCreateTime(System.currentTimeMillis());
        db.setIsHandle(0);
        userFeedbackMapper.insert(db);
    }

    public List<VideoMenuVO> getVideoMenuVOList(Long examBankId) {
        String key = RedisCommonKey.getVideoMenuKey(examBankId);
        String str = redisService.get(key);
        if (StringUtils.isBlank(str)) {
            List<VideoMenuDO> cardMenuDOS = videoMenuMapper.getVideoMenuList(examBankId);
            List<VideoMenuVO> list = cardMenuDOS.stream().map(videoMenuConverter::toView).toList();
            list = buildVideoMenuVoList(list);
            warpVideoIntro(list, examBankId);
            redisService.set(key, JSON.toJSONString(list), 60, TimeUnit.SECONDS);
            return list;
        } else {
            return JSON.parseArray(str, VideoMenuVO.class);
        }
    }

    private List<VideoMenuVO> buildVideoMenuVoList(List<VideoMenuVO> input) {
        Map<Long, VideoMenuVO> map = input.stream().collect(Collectors.toMap(VideoMenuVO::getId, v -> v));
        List<VideoMenuVO> res = new ArrayList<>();
        for (VideoMenuVO videoMenuVO : input) {
            // 如果父ID为0，说明是顶层节点，直接添加到结果列表中
            if (videoMenuVO.getParentId() == 0) {
                res.add(videoMenuVO);
            } else {
                VideoMenuVO parent = map.get(videoMenuVO.getParentId());
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(videoMenuVO);
                }
            }
        }
        return res;
    }

    private void warpVideoIntro(List<VideoMenuVO> input, Long examBankId) {
        // 只有最顶层的才有考题介绍
        Map<Long, List<VideoMenuIntroVO>> videoIntroMap = getVideoIntroMap(examBankId);
        for (VideoMenuVO item : input) {
            item.setVideoMenuIntroList(videoIntroMap.getOrDefault(item.getId(), Collections.EMPTY_LIST));
        }
    }

    private Map<Long, List<VideoMenuIntroVO>> getVideoIntroMap(Long examBankId) {
        List<VideoMenuIntroDO> videoMenuIntroDOS = videoMenuIntroMapper.selectByExamBankId(examBankId);
        Map<Long, List<VideoMenuIntroVO>> res = new HashMap<>();
        if (CollectionUtils.isNotEmpty(videoMenuIntroDOS)) {
            videoMenuIntroDOS.forEach(item -> {
                List<VideoMenuIntroVO> tmp = res.getOrDefault(item.getVideoMenuId(), new ArrayList<>());
                tmp.add(videoMenuIntroConverter.toView(item));
            });
        }
        return res;
    }

    public List<CardMenuVO> getUserWrongCardMenu(Long examBankId, long userId) {
        List<StatisticDO> userStatistic = userWrongMapper.getUserStatistic(examBankId, userId);
        return getCardMenuListByStatistic(userStatistic);
    }


    private List<CardMenuVO> getCardMenuListByStatistic(List<StatisticDO> userStatistic) {
        if (CollectionUtils.isEmpty(userStatistic)) {
            return Collections.EMPTY_LIST;
        }
        Map<Long, StatisticDO> statisticMap = userStatistic.stream().collect(Collectors.toMap(StatisticDO::getId,
                v -> v));
        Set<Long> menuIds = userStatistic.stream().map(StatisticDO::getId).collect(Collectors.toSet());
        Map<Long, CardMenuDO> menuMap = getCardMenuDOMapByIds(menuIds);
        Set<Long> allMenuIds = new HashSet<>();
        menuIds.forEach(item -> {
            CardMenuDO menuDO = menuMap.get(item);
            String[] split = menuDO.getPath().split("/");
            for (int i = 0; i < split.length; i++) {
                allMenuIds.add(Long.valueOf(split[i]));
            }
        });
        List<CardMenuDO> cardMenuDOS = cardMenuMapper.selectByIds(allMenuIds);
        List<CardMenuVO> list = cardMenuDOS.stream().map(cardMenuConverter::toView).toList();
        for (CardMenuVO item : list) {
            StatisticDO statisticDO = statisticMap.get(item.getId());
            if (statisticDO != null) {
                item.setCnt(statisticDO.getCnt());
            } else {
                item.setCnt(0);
            }
        }

        list = buildCardMenuVoList(list);
        return list;
    }

    public List<CardMenuVO> getUserCollectCardMenu(Long examBankId, long userId) {
        List<StatisticDO> userStatistic = userCollectMapper.getUserStatistic(examBankId, userId);
        return getCardMenuListByStatistic(userStatistic);
    }

    public VersionCheckVO getVersionCheck(Long channelId, Integer version) {
        String key = RedisCommonKey.getVersionCheckKey(channelId, version);
        String str = redisService.get(key);
        if (StringUtils.isNotBlank(str)) {
            return JSON.parseObject(str, VersionCheckVO.class);
        }
        SysChannelVersionDO versionDO = sysChannelVersionMapper.getLastVersion(channelId);
        if (versionDO == null) {
            log.error("未找到版本信息,渠道号 {}", channelId);
            throw new BizException(new CustomerError("500", "未找到版本信息"));
        }
        VersionCheckVO res;
        if (versionDO.getVersion() > version) {
            res = VersionCheckVO.builder()
                    .needForceUpdate(versionDO.getForceUpdate() == 1)
                    .lastVersion(versionDO.getVersionStr())
                    .downloadUrl(versionDO.getDownloadUrl())
                    .redirectUrl(versionDO.getRedirectUrl())
                    .content(versionDO.getContent())
                    .createTime(versionDO.getCreateTime())
                    .build();
        } else {
            res = VersionCheckVO.builder()
                    .needForceUpdate(false)
                    .lastVersion(versionDO.getVersionStr())
                    .build();
        }
        redisService.set(key, JSON.toJSONString(res), 60, TimeUnit.SECONDS);
        return res;
    }

    public SysBroadcastVO getSysBroadcastVO(Long channelId, Integer version) {
        String key = RedisCommonKey.getSysBroadcastKey(channelId, version);
        String str = redisService.get(key);
        if (StringUtils.isNotBlank(str)) {
            return JSON.parseObject(str, SysBroadcastVO.class);
        }
        SysBroadcastDO sysBroadcastDO = sysBroadcastMapper.getSysBroadcast(channelId, version);
        SysBroadcastVO res = new SysBroadcastVO();
        if (sysBroadcastDO != null) {
            res = sysBroadcastConverter.toView(sysBroadcastDO);
        } else {
            res.setId(-1L);
        }
        redisService.set(key, JSON.toJSONString(res), 5, TimeUnit.SECONDS);
        return res;
    }


    public Collection<VipPriceVO> getVipPriceConfigList(Long channelId) {
        String key = RedisCommonKey.getVipPriceConfigListKey(channelId);
        String str = redisService.get(key);
        if (StringUtils.isNotBlank(str)) {
            return JSON.parseArray(str, VipPriceVO.class);
        }
        SysChannelDO sysChannelDO = sysChannelMapper.selectById(channelId);
        if (sysChannelDO == null) {
            log.error("未找到渠道信息,渠道号 {}", channelId);
            throw new BizException(new CustomerError("500", "未找到渠道信息"));
        }
        Long priceGroupId = sysChannelDO.getPriceGroupId();
        List<VipPriceDO> vipPriceDOS = vipPriceMapper.selectByPriceGroupId(priceGroupId);
        Collection<VipPriceVO> res = vipPriceDOS.stream().map(vipPriceConverter::toView).toList();
        redisService.set(key, JSON.toJSONString(res), 5, TimeUnit.SECONDS);
        return res;
    }

    public VipPriceDO getVipPriceDOById(Long id) {
        return vipPriceMapper.selectById(id);
    }

    public VipPriceDO getVipPriceDOByOutId(String outId, VipPriceOutTypeEnum outType) {
        return vipPriceMapper.selectByOutId(outId, outType.getOutType());
    }

    public SysChannelDO getChannelById(Long channelId) {
        return sysChannelMapper.selectById(channelId);
    }

    public String getPayConfigById(Long aliPayConfigId) {
        SysPayConfigDO sysPayConfigDO = sysPayConfigMapper.selectById(aliPayConfigId);
        if (sysPayConfigDO == null) {
            log.error("未找到支付配置信息,id {}", aliPayConfigId);
            throw new BizException(new CustomerError("500", "未找到支付配置信息"));
        }
        return sysPayConfigDO.getConfig();
    }


    public String getOssConfig() {
        String token = redisService.get(RedisCommonKey.getOssConfigKey());
        if (StringUtils.isBlank(token)) {
            token = qiniuOssUtil.getToken();
            redisService.set(RedisCommonKey.getOssConfigKey(), token, 3500, TimeUnit.SECONDS);
        }
        return token;
    }


    public List<Long> getExamBankList() {
        return examBankMapper.getExamBankList();
    }
}
